#include "stdafx.h"
#include "TriFractal.h"

#include "Engine.h"
#include "Filesystem.h"

const uint fractal_layer = 2;
const Color background_color = Color(255, 255, 255);
const Color fractal_color = Color(0, 0, 0);

const Vector2 distance_slider(10.0f, 10.0f);
const Vector2 distance_ratio_slider(10.0f, 50.0f);
const Vector2 size_slider(10.0f, 90.0f);
const Vector2 size_ratio_slider(10.0f, 130.0f);
const Vector2 rotation_slider(10.0f, 170.0f);
const Vector2 rotation_delta_slider(10.0f, 210.0f);
const Vector2 depth_slider(10.0f, 250.0f);

TriFractal::TriFractal(RendererPtr renderer, VectorBatchPtr vector_batch, SpriteBatchPtr sprite_batch)
: renderer(renderer), vector_batch(vector_batch), sprite_batch(sprite_batch), TimelineSegment()
{
}

TriFractal::~TriFractal()
{
}

void TriFractal::init(boost::weak_ptr<Demo> owner)
{
	this->owner = owner;

	offscreen_target1 = video_device->getTextureManager()->createRenderTarget("rt0_1024x512", 1024, 512, D3DFMT_A8R8G8B8);
	offscreen_target2 = video_device->getTextureManager()->createRenderTarget("rt1_1024x512", 1024, 512, D3DFMT_A8R8G8B8);
	
	EffectPtr poisson_blur = video_device->getEffectManager()->load("PoissonBlur", Filesystem::redirect("PoissonBlur.fx"));
	poisson_blur->getAutoParams()->add(poisson_blur->getParamBySemantic("TEXTURE_SIZE"), Vector2(1024.0f, 512.0f));
	poisson_blur->getAutoParams()->add(poisson_blur->getParamBySemantic("BLUR_RADIUS"), 2.5f);
	poisson_blur->applyAutoParams();

	sprite_batch->setEffect(poisson_blur);

	fractal_position = Vector2(400.0f, 200.0f);
	fractal_distance = 64.0f;
	fractal_distance_ratio = 0.5f;
	fractal_size = 34.0f;
	fractal_size_ratio = 0.7f;
	fractal_rotation = 0.2f;
	fractal_delta_rotation = 0.1f;
	fractal_depth = 6;
}

void TriFractal::close()
{
}

void TriFractal::update(float t)
{
	if(t <= 2.7f)
	{
		fractal_depth = 1;
		fractal_distance = 0.0f;
		fractal_position.x = smoothstep(-40.0f, 300.0f, transformT(0.05f, 2.6f, t));
		fractal_position.y = smoothstep(200.0f, 260.0f, transformT(0.2f, 2.7f, t));
		fractal_rotation = smoothstep(0.0f, 2.6f, transformT(0.70f, 2.6f, t));
	}
	if(t >= 2.6f)
	{
		fractal_depth = 7;
		fractal_distance = smoothstep(0.0f, 140.0f, transformT(2.6f, 3.2f, t));
		fractal_delta_rotation = smoothstep(0.1f, 1.4f, transformT(3.1f, 4.8f, t));
		fractal_size_ratio = smoothstep(0.7f, 0.76f, transformT(5.0f, 5.7f, t));
		fractal_distance_ratio = smoothstep(0.5f, 0.6f, transformT(5.0f, 5.7f, t));
		if(t >= 5.4f)
		{		
			fractal_rotation = smoothstep(2.6f, 1.1f, transformT(5.7f, 7.1f, t));
			fractal_delta_rotation = smoothstep(1.4f, 1.2f, transformT(5.4f, 7.4f, t));
			fractal_size = smoothstep(34.0f, 10.0f, transformT(7.5f, 8.3f, t));
			fractal_size_ratio = smoothstep(0.76f, 0.9f, transformT(7.8f, 8.1f, t));
		}
		if(t >= 8.3f)
		{
			fractal_delta_rotation = 1.2f + sin((t - 8.3f) / 1.1f) * 0.6f - sin((t - 8.3f) / 1.7f) * 0.8f;
			fractal_size = smoothstep(10.0f, 32.0f, transformT(8.3f, 9.7f, t));
			fractal_size_ratio = smoothstep(0.9f, 0.7f, transformT(8.5f, 9.1f, t));
		}
		if(t >= 9.7f)
		{
			fractal_size = lerp(32.0f, 500.0f, transformT(9.7f, 10.1f, t));
		}
		if(t >= 9.92f)
		{
			fractal_depth = 6;
		}
		if(t >= 10.02f)
		{
			fractal_depth = 5;
		}
		if(t >= 10.05f)
		{
			fractal_depth = 4;
		}
		if(t >= 10.07f)
		{
			fractal_depth = 3;
		}

		fractal_position.x = 300.0f;
		fractal_position.y = 260.0f;
		fractal_position.x += sin((t - 2.6f)) * 20.0f;
		fractal_position.y += sin((t - 2.6f)/3.0f) * 16.0f;
	}	
}

void TriFractal::render(float t)
{
	// Ugly way to draw fullscreen rect
	Vector2 p1(0.0f, 0.0f);
	Vector2 p2(1024.0f, 0.0f);
	Vector2 p3(1024.0f, 576.0f);
	Vector2 p4(0.0f, 576.0f);

	vector_batch->drawTriangle(0, p1, p2, p3); 
	vector_batch->drawTriangle(0, p3, p4, p1); 
	drawFractal(fractal_position, fractal_distance, fractal_distance_ratio, fractal_size, fractal_size_ratio,
		fractal_rotation, fractal_delta_rotation, fractal_depth);
	vector_batch->present();

	video_device->setClearColor(background_color);

	video_device->beginScene(true, false, false, offscreen_target1);
	renderer->render();
	video_device->endScene();

	video_device->beginScene(true, false, false, offscreen_target2);
	sprite_batch->draw(offscreen_target1, 3, RectF(), RectF(0.0f, 0.0f, 1024.0f, 576.0f), Color(255, 255, 255, 255));
	sprite_batch->present();
	renderer->render(1003, 1003);
	video_device->endScene();

	video_device->setClearColor(Color(0, 0, 0));
	video_device->beginScene(true, false);
	sprite_batch->draw(offscreen_target2, 1, RectF(), RectF(0.0f, 0.0f, 1024.0f, 576.0f), Color(255, 255, 255, 255));
	sprite_batch->present();
	renderer->render(1000, 1002);
	video_device->endScene();

	renderer->endFrame();
}

void TriFractal::drawFractal(const Vector2& pos, float distance, float distance_ratio, 
	float size, float size_ratio, float rotation, float delta_rotation, int depth)
{
	if(depth == 0)
		return;

	Vector2 vec1(0.0f, -1.0f);
	Vector2 vec2 = vec1.getRotated(rotation + pi * 2.0f / 3.0f);
	Vector2 vec3 = vec1.getRotated(rotation - pi * 2.0f / 3.0f);
	vec1.rotate(rotation);

	vector_batch->drawTriangle(fractal_layer, pos + (vec1 * size), pos + (vec2 * size), pos + (vec3 * size), fractal_color);

	vec1 = pos + (vec1 * distance);
	vec2 = pos + (vec2 * distance);
	vec3 = pos + (vec3 * distance);

	drawFractal(vec1, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
		rotation + delta_rotation, delta_rotation, depth - 1);
	drawFractal(vec2, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
		rotation + delta_rotation, delta_rotation, depth - 1);
	drawFractal(vec3, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
		rotation + delta_rotation, delta_rotation, depth - 1);
}